package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
)

var (
	is_Win     bool
	name_GOVER string
	path_GOBIN string
	// C_GOPATH string
)

//// linux
//const (
// 	C_Flag_Prefix   = "-" // unix
// 	C_Flag_Slient   = "-c"
//  C_Exec_Prefix   = "./"
// 	C_File_Suffix   = ""
// 	C_Exec_FullPath = "sh"
// 	C_Golang_Exe    = "go"
// 	C_Split_String  = "/"
//  C_GoPath_Split  = ":"
//
//  C_GOPATH = "G:/working/projectgo/goget;G:/working/projectgo/prowork;C:/Go"
//)

// // mac
// const (
// 	C_Flag_Prefix   = "-" // unix
// 	C_Flag_Slient   = "-c"
//  C_Exec_Prefix   = "./"
// 	C_File_Suffix   = ""
// 	C_Exec_FullPath = "sh"
// 	C_Golang_Exe    = "go"
// 	C_Split_String  = "/"
//  C_GoPath_Split  = ":"

// 	C_GOPATH = "G:/working/projectgo/goget;G:/working/projectgo/prowork;C:/Go"
// )

// windows
const (
	//C_Flag_Prefix   = "-" // unix
	C_Flag_Slient   = "/c"
	C_Exec_Prefix   = ""
	C_File_Suffix   = ".exe"
	C_Exec_FullPath = "cmd.exe"
	C_Golang_Exe    = "go.exe"
	C_Split_String  = "\\"
	C_GoPath_Split  = ";"

	C_GOPATH = "C:\\develop\\working\\goget;C:\\develop\\working\\prowork;C:\\develop\\working\\prjdev;C:\\Go"
)

func main() {
	fmt.Println("^_^_^_^ start gogen ^_^_^_^")

	path := os.Getenv("GOPATH")
	if path == "" {
		os.Setenv("GOPATH", C_GOPATH)
	}

	gohosts := os.Getenv("GOHOSTS")
	if gohosts == "windows" {
		is_Win = true
	}

	doWork()
}

func doWork() {
	name_GOVER := runtime.Version()
	path_GOBIN = os.Getenv("GOBIN")
	fmt.Println("GOVER = ", name_GOVER)

	if C_GOPATH == "" {
		fmt.Println("gogen error = GOPATH no set")
		return
	}
	fmt.Println("GOPATH = ", C_GOPATH)

	cmds := os.Args
	cmdsLen := len(os.Args)

	if cmdsLen == 1 {
		fmt.Println("gogen error = Parse Args Failed")
		return
	}

	// 取倒数第二个命令行参数，为源文件所在路径"$file_path"
	fileDir := doTransSplitSymbol(cmds[cmdsLen-1], C_Split_String, "/")

	// 取最后一个命令行参数，为源文件名称"$file_name"
	fileName := cmds[cmdsLen-2]

	// 得到当前源文件的工作路径
	workdir := doAutoGetWorkDir(fileDir)
	os.Setenv("GOBIN", workdir)

	fmt.Println("fileDir = ", fileDir)
	fmt.Println("workdir = ", workdir)
	//fmt.Println("gogen_dir = ", path_gogen)

	StartCompile(fileName, fileDir, workdir)

	os.Setenv("GOBIN", path_GOBIN)
}

func StartCompile(fileName, fileDir, workdir string) {

	bCompile := false
	workexe := "" //path_gobin + "go.exe"
	golangCmd := doGetGolangCmd(fileDir, workdir)
	fmt.Println("go exec cmd = ", workexe+golangCmd)

	fmt.Printf("\n")
	if strings.Contains(golangCmd, "run") {
		doFmt(fileName, fileDir)
		bCompile = doRun(fileName, fileDir, workdir, golangCmd)
	} else if strings.Contains(golangCmd, "install") {
		doFmt(fileName, fileDir)
		bCompile = doInstall(fileName, fileDir, workdir, golangCmd)
	} else if strings.Contains(golangCmd, "clean") {
		bCompile = doClean(fileDir, golangCmd)
	} else {
		doFmt(fileName, fileDir)
		bCompile = doExecCommand(golangCmd, C_Flag_Slient)
	}

	if bCompile {
		fmt.Println(workexe + golangCmd + " nice ^_^_^")
	}

	fmt.Printf("\n")
	fmt.Println("^_^_^ gogen compile result = ", bCompile)
}

func doFmt(fileName, fileDir string) {
	golangCmd := "gofmt -w " + fileDir + "/" + fileName
	doExecCommand(golangCmd, C_Flag_Slient)
}

func doRun(fileName, fileDir, workdir, golangCmd string) bool {
	golangCmd = strings.Replace(golangCmd, "run", "install", -1)
	if false == doInstall(fileName, fileDir, workdir, golangCmd) {
		return false
	}

	// 先定位到workdir,再执行exe
	cdcmd := "cd " + workdir
	pkgname := doGetPkgName(fileDir)
	runexe := C_Exec_Prefix + filepath.Base(pkgname + C_File_Suffix)
	return doExecCommand(cdcmd+"&&"+runexe, C_Flag_Slient)
}

func doInstall(fileName, fileDir, workdir, golangCmd string) bool {
	pkgname := doGetPkgName(fileDir)
	fmt.Println("package name = ", pkgname)

	golangCmd = strings.Replace(golangCmd, fileName, pkgname, -1)
	bOk := doExecCommand(golangCmd, C_Flag_Slient)

	prjdir := doGetProjectDir(fileDir)
	if fileDir != prjdir+"/src/"+pkgname {
		return bOk
	}

	if !strings.Contains(name_GOVER, "1.5") {
		return bOk
	}

	// copy file to workdir, go 1.5.3 need copy file
	_, err := doCopyFile(path_GOBIN+pkgname+C_File_Suffix, workdir+pkgname+C_File_Suffix)
	if err != nil {
		fmt.Println("go install failed with copy file error:", err)
		return false
	}
	return bOk
}

func doClean(fileDir, golangCmd string) bool {
	doExecCommand(golangCmd, C_Flag_Slient)
	fileDir = doTransSplitSymbol(fileDir, C_Split_String, "/")
	prjDir := doGetProjectDir(fileDir)
	err := filepath.Walk(prjDir+"/pkg/", func(path string, f os.FileInfo, err error) error {
		if f == nil {
			return err
		}

		os.Remove(path)
		return nil
	})

	if err != nil {
		fmt.Println("do Clean Failed With:", err.Error())
		return false
	}
	return true
}

func doExecCommand(strcmd, flag string) bool {
	cmd := exec.Command(C_Exec_FullPath, flag, strcmd)
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	cmd.Stdin = os.Stdin
	err := cmd.Run()
	if err != nil {
		//fmt.Println("exec workexe compile error = ", err.Error())
		return false
	}
	return true
}

func doGetGolangCmd(fileDir, workdir string) string {
	// 获取编译参数
	buildcmds := os.Args[1 : len(os.Args)-2]
	//fmt.Println("buildcmds is = ", buildcmds)

	workcmd := ""
	for _, v := range buildcmds {
		workcmd += " " + v
	}
	workcmd = strings.TrimLeft(workcmd, " ")
	fileDir = doTransSplitSymbol(fileDir, C_Split_String, "/")
	prjDir := doGetProjectDir(fileDir)

	if fileDir != prjDir+"/src" {
		prjDir = prjDir + "/src/"
	}

	buildpkg := strings.TrimPrefix(fileDir, prjDir)
	return "go " + workcmd + " " + buildpkg
}

// 获取当前文件执行的路径
func doGetCurrPath() string {
	file, _ := exec.LookPath(os.Args[0])
	path, _ := filepath.Abs(file)

	splitstr := strings.Split(path, C_Split_String)
	splitsize := len(splitstr)
	splitstr = strings.Split(path, splitstr[splitsize-1])
	ret := strings.Replace(splitstr[0], C_Split_String, "/", splitsize-1)
	return ret
}

func doGetPkgName(filedir string) string {
	splitstr := strings.Split(filedir, C_Split_String)
	splitsize := len(splitstr)
	// fmt.Println("pkgname .....:  ", splitstr, splitsize, filedir)

	if true == strings.HasSuffix(filedir, C_Split_String) {
		return splitstr[splitsize-2]
	} else {
		return splitstr[splitsize-1]
	}

}

func doTransSplitSymbol(path, oldsplit, newsplit string) string {
	retstr := strings.Replace(path, oldsplit, newsplit, -1)
	return retstr
}

func doGetProjectDir(fileDir string) string {
	workdir := ""
	arrdirs := strings.Split(C_GOPATH, C_GoPath_Split)

	for _, v := range arrdirs {
		v = doTransSplitSymbol(v, C_Split_String, "/")
		// fmt.Println("====test=====", v, fileDir)
		if strings.Contains(fileDir, v) {
			workdir = v
			break
		}
	}
	return workdir
}

// 遍历GOPATH，返回工程路径
func doAutoGetWorkDir(fileDir string) string {
	//fileDir = doTransSplitSymbol(fileDir, C_Split_String, "/")
	//fmt.Println("====test=====", transpath)

	workdir := doGetProjectDir(fileDir)
	return workdir + C_Split_String + "bin" + C_Split_String
}

func doCopyFile(src, dst string) (w int64, err error) {
	srcFile, err := os.Open(src)
	if err != nil {
		//fmt.Println(err.Error())
		return
	}
	defer srcFile.Close()

	dstFile, err := os.Create(dst)

	if err != nil {
		//fmt.Println(err.Error())
		return
	}

	defer dstFile.Close()

	return io.Copy(dstFile, srcFile)
}
